import java.io.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import java.util.Calendar;
import java.text.SimpleDateFormat;

public class RinexGPSNavFile {
    
    private String fileName;

    private static String versionStr     = "RINEX VERSION / TYPE";
    private static String programStr     = "PGM / RUN BY / DATE";
    private static String commentStr     = "COMMENT";
    private static String ionoCorrStr    = "IONOSPHERIC CORR";
    private static String timeCorrStr    = "TIME SYSTEM CORR"; 
    private static String leapSecondsStr = "LEAP SECONDS"; 
    private static String eohStr         = "END OF HEADER";

    // Flag will be changed to true if END OF HEADER is detected
    private int eohFlag = 0;
    private int lineCtr = 0;
    private int records = 0;

    private static Pattern pStartRecord = Pattern.compile("^\\s+\\d+\\s+\\d+\\s+\\d+");
    private static Pattern pStartRecord2 = Pattern.compile("^\\d+\\s+\\d+\\s+\\d+");
    private static Pattern pPRN = Pattern.compile("^\\s+\\d+\\s+");
    private static Pattern pPRN2 = Pattern.compile("^\\s+\\d+\\s+");

    private static StringUtils util;

    private NavDataGPS[] nDataArray; 

    RinexGPSNavFile(String filename) {
        fileName = filename;
    }

    public void readGPSNavFile() {
        try {
            File file = new File(fileName);
            FileReader reader = new FileReader(file);
            BufferedReader in = new BufferedReader(reader);
            String line;
            //
            // Do a first pass to work out how many records are in this navigation file
            //

            while ( (line = in.readLine()) != null) {
                // Check to see if we are trying to parse part of the Nav Header
                if(eohFlag == 0 && line.length() > 60) { 
                    String endOfLine = line.substring(60);
                    if( eohStr.matches(endOfLine) ) {
                        eohFlag = 1;
                    }
                }
                else if(eohFlag == 1) {
                    Matcher m = pStartRecord.matcher(line);
                    Matcher m2 = pStartRecord2.matcher(line);
                    if(m.find()) {
                        records++;
                    }
                    else if(m2.find()) {
                        records++;
                    }
                }
            }
            in.close();

            //
            // re-initialise some variables for the next pass of the file
            //
            eohFlag = 0;
            reader = new FileReader(file);
            in = new BufferedReader(reader);

            nDataArray = new NavDataGPS[records];
            System.out.println("Number of Nav Records:"+records);
            records = 0;

            while ( (line = in.readLine()) != null) {
                // Check to see if we are trying to parse part of the Nav Header
                int length = line.length();
                if(eohFlag == 0 && length > 60) { 
                    String endOfLine = line.substring(60);
                    if( eohStr.matches(endOfLine) ) {
                        eohFlag = 1;
                    }
                }
                else if(eohFlag == 1) {
                    Matcher m = pStartRecord.matcher(line);
                    Matcher m2 = pStartRecord2.matcher(line);
                    if(m.find()) {
                        _parseStartRecord(line);
                    }
                    // Look for lines which start with the prn, but no space
                    else if (m2.find()) {
                        _parseStartRecord(line);
                    }
                    else {
                        _parseGPSNavLine(line);
                    }

                }
            }
            in.close();

        }
        catch(IOException e) {
            e.printStackTrace();
        }
    }

    private void  _parseStartRecord(String line) {
        // Satellite PRN number
        Integer prn = Integer.parseInt( util.lrtrim( line.substring(0,2) ) );
        //System.out.print("PRN:"+prn+": ");

        //
        // Epoch: Time of Clock
        //
        Integer YY = Integer.parseInt( util.lrtrim( line.substring(3,5) ) );
        Integer MM = Integer.parseInt( util.lrtrim( line.substring(6,8) ) ); 
        Integer DD = Integer.parseInt( util.lrtrim( line.substring(9,11) ) ); 
        Integer hh = Integer.parseInt( util.lrtrim( line.substring(12,14) ) ); 
        Integer mm = Integer.parseInt( util.lrtrim( line.substring(15,17) ) ); 
        Integer ss = Integer.parseInt( util.lrtrim( line.substring(18,20) ) ); 
        Integer ms = Integer.parseInt( util.lrtrim( line.substring(21,22) ) ); 

        //
        // Ok now convert to millis
        //
        Calendar cal = Calendar.getInstance();
        int year;
        if( YY < 80 ) { year = YY + 2000; }
        else { year = YY + 1900; }    
        
        cal.set(Calendar.YEAR,year);
        cal.set(Calendar.MONTH,MM);
        cal.set(Calendar.DAY_OF_MONTH,DD);
        cal.set(Calendar.HOUR_OF_DAY,hh);
        cal.set(Calendar.MINUTE,mm);
        cal.set(Calendar.SECOND,ss);
        cal.set(Calendar.MILLISECOND,ms);
        long timeInMilliS = cal.getTimeInMillis();
        double time = timeInMilliS / 1000.0;
        NavDataGPS nData = new NavDataGPS(prn,time);
        nDataArray[records] = nData;

        Double clockBias = Double.parseDouble( util.lrtrim( line.substring(22,41) ).replace('D','E') );
        //System.out.print("clockBias:"+clockBias+": ");

        Double clockDrift = Double.parseDouble( util.lrtrim( line.substring(41,60) ).replace('D','E') );
        //System.out.print("clockDrift:"+clockDrift+": ");

        Double clockDriftRate = Double.parseDouble( util.lrtrim( line.substring(60,79) ).replace('D','E') ); 
        //System.out.println("clockDriftRate:"+clockDriftRate+": ");

        nData.firstLine(clockBias,clockDrift,clockDriftRate);
        lineCtr=1;
    }   

    private void _parseGPSNavLine(String line) {
        //System.out.println("LINE:"+line); 
        String r1 = (util.lrtrim( line.substring(3,22) )).replace('D','E');
        //System.out.print("r1:"+r1+": ");
        String r2 = null;
        String r3 = null;
        String r4 = null;

        if(line.length() > 22) {
            r2 = util.lrtrim( line.substring(22,41) ).replace('D','E');
            //System.out.print("r2:"+r2+": ");
            r3 = util.lrtrim( line.substring(41,60) ).replace('D','E');
            //System.out.print("r3:"+r3+": ");
            r4 = util.lrtrim( line.substring(60,79) ).replace('D','E') ; 
            //System.out.print("r4:"+r4+": ");
        }
        //System.out.println();

        if(lineCtr == 1) {
            nDataArray[records].setIODE( Double.parseDouble( r1 ) );
            nDataArray[records].setCrs( Double.parseDouble( r2 ) );
            nDataArray[records].setdN( Double.parseDouble( r3 ) );
            nDataArray[records].setm0( Double.parseDouble( r4 ) );
        }
        else if(lineCtr == 2) {
            nDataArray[records].setCuc( Double.parseDouble( r1 ) );
            nDataArray[records].setecc( Double.parseDouble( r2 ) );
            nDataArray[records].setCus( Double.parseDouble( r3 ) );
            nDataArray[records].setsqrtA( Double.parseDouble( r4 ) );
        }
        else if(lineCtr == 3) {
            nDataArray[records].setToe( Double.parseDouble( r1 ) );
            nDataArray[records].setCic( Double.parseDouble( r2 ) );
            nDataArray[records].setOMEGA( Double.parseDouble( r3 ) );
            nDataArray[records].setCIS( Double.parseDouble( r4 ) );
        }
        else if(lineCtr == 4) {
            nDataArray[records].seti0( Double.parseDouble( r1 ) );
            nDataArray[records].setCrc( Double.parseDouble( r2 ) );
            nDataArray[records].setomega( Double.parseDouble( r3 ) );
            nDataArray[records].setOMEGADot( Double.parseDouble( r4 ) );
        }
        else if(lineCtr == 5) {
            nDataArray[records].setidot( Double.parseDouble( r1 ) );
            nDataArray[records].setl2channel( Double.parseDouble( r2 ) );
            nDataArray[records].setgpswk( Double.parseDouble( r3 ) );
            nDataArray[records].setl2pFlag( Double.parseDouble( r4 ) );
        }
        else if(lineCtr == 6) {
            nDataArray[records].setsva( Double.parseDouble( r1 ) );
            nDataArray[records].setsvh( Double.parseDouble( r2 ) );
            nDataArray[records].settgd( Double.parseDouble( r3 ) );
            nDataArray[records].setiodc( Double.parseDouble( r4 ) );
        }
        else if(lineCtr == 7) {
            nDataArray[records].setzcount( Double.parseDouble( r1 ) );
            if(line.length() > 22) {
                nDataArray[records].setfit( Double.parseDouble( r4 ) );
                nDataArray[records].setspare1( Double.parseDouble( r4 ) );
                nDataArray[records].setspare2( Double.parseDouble( r4 ) );
            }
            records++;
        }
        //System.out.println();
        lineCtr++;
    }
  
    public int findNavData(int PRN, long epoch) {
        for( int i = 0 ; i < records ; i++) {
            NavDataGPS nData = nDataArray[i];
            if(nData.prn() == PRN) {
                return i;
            }            
        }
        return -1;
    }

    public void findNavData(int PRN) {
        for( int i = 0 ; i < records ; i++) {
            NavDataGPS nData = nDataArray[i];
            if(nData.prn() == PRN) {
                System.out.println("PRN index: "+i+"  TOC: "+nData.toc());
            }            
        }
    }

    public NavDataGPS getNavData(int index) {
        NavDataGPS nData = nDataArray[index];
        return nData;
    }
 
}



